﻿using System;
using HzyScanDiService.Attributes;
using HzyScanDiService.Models;
using Natasha.CSharp;
using Natasha.CSharp.Builder;
using System.Linq;

namespace HzyScanDiService.Impl
{
    /// <summary>
    /// 动态代理类 实现类
    /// </summary>
    public class DynamicProxyImpl : IDynamicProxy
    {
        public NClass GenerateDynamicProxy(ClassContext classContext)
        {
            //获取父类构造函数的参数
            var pars = classContext.GetParametersByConstructor();
            //定义构造函数
            var ctorBuilder = new CtorBuilder();
            ctorBuilder.Public();
            ctorBuilder.Constraint(":base(" + string.Join(",", pars.Select(w => w.Name)) + ")");
            ctorBuilder.Body(@"
                var classType = base.GetType();
                _classContext = new ClassContext(classType);
                _aopBaseAttribute = classType.GetCustomAttributes<AopBaseAttribute>()?.FirstOrDefault();
            ");
            foreach (var item in pars)
            {
                ctorBuilder.Param(item.ParameterType, item.Name);
            }
            var namespaceHzyScanDiService = nameof(HzyScanDiService);
            //创建类
            NClass nClass = NClass.DefaultDomain();
            nClass
              .Using(namespaceHzyScanDiService + ".Models")
              .Using(namespaceHzyScanDiService + ".Attributes")
              .Using(namespaceHzyScanDiService)
              .Namespace(namespaceHzyScanDiService + ".DynamicProxy")
              .Public()
              .Access("")
              .Class()
              .Name(classContext.GetClassName() + "Proxy")
              .Field(f => f.Type(typeof(ClassContext)).Name("_classContext").Public().Readonly())
              .Field(f => f.Type(typeof(AopBaseAttribute)).Name("_aopBaseAttribute").Public().Readonly())
              .Ctor(ctorBuilder)
              //继承
              .InheritanceAppend(classContext.ClassType)
              .Body(@"
                    private void Before(Dictionary<string, object> parameter , string methodName) => _aopBaseAttribute.Before(_classContext , parameter , methodName);
                    private void After(object result , string methodName) => _aopBaseAttribute.After(_classContext , result , methodName);
                    private void Exception(Exception exception , string methodName) => _aopBaseAttribute.ExceptionEvent?.Invoke(_classContext , exception , methodName);
              ")
              ;

            //重写函数
            foreach (var item in classContext.GetFunctions())
            {
                var function = new MethodBuilder()
                   .Public()
                   .Override()
                   .Name(item.Name)
                   ;

                function.BodyAppend(item.ParameterInfo.Length > 0 ? "var resultDic = new Dictionary<string, object>();" : "");
                foreach (var parameterInfo in item.ParameterInfo)
                {
                    function.Param(parameterInfo.ParameterType, parameterInfo.Name);
                    function.BodyAppend($@"resultDic[""{parameterInfo.Name}""] = {parameterInfo.Name};");
                }

                var methodName = "nameof(" + item.Name + ")";

                //调用前处理 Before
                if (item.ParameterInfo.Length > 0)
                {
                    function.BodyAppend($"this.Before(resultDic , {methodName});");
                }
                else
                {
                    function.BodyAppend($"this.Before(null , {methodName});");
                }

                var isExceptionEventNull = classContext.GetAopBaseAttribute()?.ExceptionEvent == null;
                //调用后处理 After
                if (item.ResultType == typeof(void))
                {
                    this.HndleException(isExceptionEventNull, function, methodName, () =>
                    {
                        function.BodyAppend($"base.{item.Name}({string.Join(",", item.ParameterInfo.Select(w => w.Name))});");
                        function.BodyAppend($"this.After(null , {methodName});");

                    });
                }
                else
                {
                    this.HndleException(isExceptionEventNull, function, methodName, () =>
                    {
                        function.BodyAppend($"var result = base.{item.Name}({string.Join(",", item.ParameterInfo.Select(w => w.Name))});");
                        function.BodyAppend($"this.After(result , {methodName});");
                        //返回值
                        function.BodyAppend("return result;");

                    });

                    function.Return(item.ResultType);
                }

                nClass.Method(function);
            }

            var str = nClass.Script;

            return nClass;
        }

        /// <summary>
        /// 处理异常拼接
        /// </summary>
        /// <param name="isExceptionEventNull"></param>
        /// <param name="function"></param>
        /// <param name="methodName"></param>
        /// <param name="body"></param>
        private void HndleException(bool isExceptionEventNull, MethodBuilder function, string methodName, Action body)
        {
            function.BodyAppend(isExceptionEventNull ? "" : "try{");

            body.Invoke();

            function.BodyAppend(isExceptionEventNull ? "" : "}catch (System.Exception ex){");

            function.BodyAppend(isExceptionEventNull ? "" : $"this.Exception(ex,{methodName});");

            function.BodyAppend(isExceptionEventNull ? "" : "throw;}");
        }


    }


}
